using Microsoft.EntityFrameworkCore;
using PioneerBoilerplate.Model.Admin;
using PioneerBoilerplate.Repository;
using PioneerBoilerplate.Repository.Repositories;
using PioneerBoilerplate.Service.Admin.Document.Input;
using PioneerBoilerplate.Service.Admin.Document.Output;
using PioneerBoilerplate.Service.Base;
using PioneerBoilerplate.Util;
using PioneerBoilerplate.Util.Output;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace PioneerBoilerplate.Service.Admin.Document
{
    public class DocumentService : BaseService, IDocumentService
    {
        private readonly IDocumentRepository _documentRepository;
        private readonly IDocumentImageRepository _documentImageRepository;

        public DocumentService(
            IDocumentRepository DocumentRepository,
            IDocumentImageRepository documentImageRepository
        )
        {
            _documentRepository = DocumentRepository;
            _documentImageRepository = documentImageRepository;
        }

        public async Task<IResponseOutput> GetAsync(long id)
        {
            var result = await _documentRepository.FirstOrDefaultAsync(a => a.Id == id);

            return ResponseOutput.Ok(result);
        }

        public async Task<IResponseOutput> GetGroupAsync(long id)
        {
            var result = Mapper.Map<DocumentGetGroupOutput>(await _documentRepository.FirstOrDefaultAsync(a => a.Id == id));
            return ResponseOutput.Ok(result);
        }

        public async Task<IResponseOutput> GetMenuAsync(long id)
        {
            var result = Mapper.Map<DocumentGetMenuOutput>(await _documentRepository.FirstOrDefaultAsync(a => a.Id == id));
            return ResponseOutput.Ok(result);
        }

        public async Task<IResponseOutput> GetContentAsync(long id)
        {
            var result =Mapper.Map<DocumentGetContentOutput>(await _documentRepository.FirstOrDefaultAsync(a => a.Id == id));
            return ResponseOutput.Ok(result);
        }

        public async Task<IResponseOutput> GetListAsync(string key, DateTime? start, DateTime? end)
        {
            if (end.HasValue)
            {
                end = end.Value.AddDays(1);
            }

            PaginationSpecification<DocumentEntity> specification = new();
            specification.WhereIf(key.NotNull(), e => e.Name.Contains(key) || e.Label.Contains(key));
            specification.WhereIf(end.HasValue&& start.HasValue, e => 
                                                  e.CreatedTime.Value >= start.Value && 
                                                  e.CreatedTime.Value <= end.Value);
            specification.OrderBy = q => q.OrderBy(e => e.ParentId)
                         .ThenBy(e => e.Sort);
            specification.PageIndex = 1;
            specification.PageSize = 1000000;

            var list = await _documentRepository.GetListAsync(specification);
            var data = list.Items.Select(a => new DocumentListOutput
            {
                Id = a.Id,
                Description = a.Description,
                Label = a.Label,
                Name = a.Name,
                Opened = a.Opened,
                ParentId = a.ParentId
            }); ;

            return ResponseOutput.Ok(data);
        }

        public async Task<IResponseOutput> GetImageListAsync(long id)
        {
            var result = await _documentImageRepository.GetAll()
                .Where(a => a.DocumentId == id)
                .OrderByDescending(a => a.Id)
                .Select(a => a.Url)
                .ToListAsync();

            return ResponseOutput.Ok(result);
        }

        public async Task<IResponseOutput> AddGroupAsync(DocumentAddGroupInput input)
        {
            var entity = Mapper.Map<DocumentEntity>(input);
            var id = (await _documentRepository.InsertAsync(entity)).Id;

            return ResponseOutput.Result(id > 0);
        }

        public async Task<IResponseOutput> AddMenuAsync(DocumentAddMenuInput input)
        {
            var entity = Mapper.Map<DocumentEntity>(input);
            var id = (await _documentRepository.InsertAsync(entity)).Id;

            return ResponseOutput.Result(id > 0);
        }

        public async Task<IResponseOutput> AddImageAsync(DocumentAddImageInput input)
        {
            var entity = Mapper.Map<DocumentImageEntity>(input);
            var id = (await _documentImageRepository.InsertAsync(entity)).Id;

            return ResponseOutput.Result(id > 0);
        }

        public async Task<IResponseOutput> UpdateGroupAsync(DocumentUpdateGroupInput input)
        {
            var result = false;
            if (input != null && input.Id > 0)
            {
                var entity = await _documentRepository.FirstOrDefaultAsync(a => a.Id == input.Id);
                entity = Mapper.Map(input, entity);
                result = (await _documentRepository.UpdateAsync(entity)) != null;
            }

            return ResponseOutput.Result(result);
        }

        public async Task<IResponseOutput> UpdateMenuAsync(DocumentUpdateMenuInput input)
        {
            var result = false;
            if (input != null && input.Id > 0)
            {
                var entity = await _documentRepository.FirstOrDefaultAsync(a => a.Id == input.Id);
                entity = Mapper.Map(input, entity);
                result = (await _documentRepository.UpdateAsync(entity)) != null;
            }

            return ResponseOutput.Result(result);
        }

        public async Task<IResponseOutput> UpdateContentAsync(DocumentUpdateContentInput input)
        {
            var result = false;
            if (input != null && input.Id > 0)
            {
                var entity = await _documentRepository.FirstOrDefaultAsync(a => a.Id == input.Id);
                entity = Mapper.Map(input, entity);
                result = (await _documentRepository.UpdateAsync(entity)) != null;
            }

            return ResponseOutput.Result(result);
        }

        public async Task<IResponseOutput> DeleteAsync(long id)
        {
            if (id > 0)
            {
                var entity = await _documentRepository.FirstOrDefaultAsync(a => a.Id == id);
                await _documentRepository.DeleteAsync(entity);
            }

            return ResponseOutput.Result(true);
        }

        public async Task<IResponseOutput> DeleteImageAsync(long documentId, string url)
        {
            if (documentId > 0 && url.NotNull())
            {
                var entity = await _documentImageRepository.FirstOrDefaultAsync(m => m.DocumentId == documentId && m.Url == url);
                await _documentImageRepository.DeleteAsync(entity);
            }

            return ResponseOutput.Result(true);
        }

        public async Task<IResponseOutput> SoftDeleteAsync(long id)
        {
            var entity = await _documentRepository.FirstOrDefaultAsync(a => a.Id == id);
            entity.IsDeleted = true;
            var result = (await _documentRepository.UpdateAsync(entity)) != null;
            return ResponseOutput.Result(result);
        }

        public async Task<IResponseOutput> GetPlainListAsync()
        {
            var documents = await _documentRepository.GetAll()
                .OrderBy(a => a.ParentId)
                .ThenByDescending(a => a.Sort)
                .Select(a => new { a.Id, a.ParentId, a.Label, a.Type, a.Opened })
                .ToListAsync();

            var menus = documents
                .Where(a => (new[] { DocumentType.Group, DocumentType.Markdown }).Contains(a.Type))
                .Select(a => new
                {
                    a.Id,
                    a.ParentId,
                    a.Label,
                    a.Type,
                    a.Opened
                });

            return ResponseOutput.Ok(menus);
        }
    }
}